package com.camundademo.rest.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.camundademo.modole.dto.DefineFlowInput;
import jakarta.validation.constraints.NotBlank;
import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.DeploymentBuilder;
import org.camunda.bpm.engine.repository.DeploymentQuery;
import org.camunda.bpm.engine.repository.DeploymentWithDefinitions;
import org.camunda.bpm.engine.repository.Resource;
import org.camunda.bpm.engine.rest.dto.repository.DeploymentDto;
import org.camunda.bpm.engine.rest.dto.repository.DeploymentQueryDto;
import org.camunda.bpm.engine.rest.dto.repository.DeploymentResourceDto;
import org.camunda.bpm.engine.rest.dto.repository.DeploymentWithDefinitionsDto;
import org.camunda.bpm.engine.rest.util.QueryUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

@Service
public class CamundaFlowService {

    /**
     * 仓库服务,流程部署
     */
    @Autowired
    private RepositoryService repositoryService;

    /**
     * 运行时服务
     */
    @Autowired
    private RuntimeService runtimeService;

    /**
     * 流程引擎实例  可以获取各种服务
     */
    @Autowired
    private ProcessEngine processEngine;


    /**
     * 部署 就是把bpmn文件加入到数据库表中
     */
    public DeploymentWithDefinitionsDto deploy(DefineFlowInput defineFlowInput) throws IOException {

        DeploymentBuilder camundaBuilder = repositoryService.createDeployment().name(defineFlowInput.getName())//部署名称
                .source(defineFlowInput.getSource())//部署来源
                //去除空串
                .tenantId(StrUtil.isBlank(defineFlowInput.getTenantId()) ? null : defineFlowInput.getTenantId())//租户ID

                /**
                 * 当这个标志被设置为 true 时，Camunda 流程引擎会检查是否已经存在一个同名的部署，并且包含相同的资源。
                 * 如果发现存在这样的部署，引擎不会创建一个新的部署，而是返回现有的部署。
                 */.enableDuplicateFiltering(true);// 默认false

        if (CollectionUtil.isNotEmpty(defineFlowInput.getFiles())) {
            for (MultipartFile file : defineFlowInput.getFiles()) {
                //bpmn的文件流  多个文件调用多次
                camundaBuilder.addInputStream(
                        //file.getOriginalFilename()上传时的原文件名
                        file.getOriginalFilename(), file.getInputStream());
                //.addString("","") //bpmn的内容字符串
            }
        }

        DeploymentWithDefinitions deploymentWithDefinitions = camundaBuilder
                //.deploy() //简单的部署
                .deployWithResult();//部署后详细的信息

        System.out.println(deploymentWithDefinitions);

        DeploymentWithDefinitionsDto deploymentDto = DeploymentWithDefinitionsDto.fromDeployment(
                deploymentWithDefinitions);
        System.out.println(deploymentDto);

        return deploymentDto;
    }

    public void delDeploy(String deploymentId) {
        repositoryService.deleteDeployment(
                //部署id
                deploymentId,
                // 是否级联删除 true 则不仅删除部署本身，还会删除所有相关的运行时和历史数据，包括流程实例、任务实例、历史记录
                true,
                //如果为true，则只有内置的ExecutionListener会收到通知。EVENTNAME_END事件。
                true,
                //是否跳过  部署的io映射
                false
                                          );
    }


    public List<DeploymentDto> queryDeploy(
            DeploymentQueryDto deploymentQueryDto,
            Integer firstResult,
            Integer maxResults
                                          ) {

        DeploymentQuery query = (DeploymentQuery) deploymentQueryDto.toQuery(processEngine);
        List<Deployment> matchingDeployments = QueryUtil.list(query, firstResult, maxResults);
        List<DeploymentDto> deployments = new ArrayList();

        for (Deployment deployment : matchingDeployments) {
            DeploymentDto def = DeploymentDto.fromDeployment(deployment);
            deployments.add(def);
        }

        return deployments;
    }

    public List<DeploymentResourceDto> getDeploymentResources(String deploymentId) {
        List<Resource> deploymentResources = repositoryService.getDeploymentResources(deploymentId);

        List<DeploymentResourceDto> deploymentResourceDtoList = new ArrayList();
        for (Resource deploymentResource : deploymentResources) {
            DeploymentResourceDto deploymentResourceDto = DeploymentResourceDto.fromResources(deploymentResource);
            deploymentResourceDtoList.add(deploymentResourceDto);
        }

        return deploymentResourceDtoList;
    }

    public InputStream getResourceAsStreamById(
            @NotBlank(message = "deploymentId不能空")
            String deploymentId,
            @NotBlank(message = "resourcesId不能空")
            String resourcesId
                                              ) {
        InputStream resourceAsStream = repositoryService.getResourceAsStreamById(deploymentId, resourcesId);

        return resourceAsStream;
    }
}
